package cn.bytengine.stem.cluster.netty;

import cn.bytengine.stem.contexts.Context;
import cn.bytengine.stem.sdso.*;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.map.MapBuilder;
import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import static cn.bytengine.stem.utils.Address.localAddress;

public class NettyClusterManager implements ClusterManager {
    private static final Logger log = LoggerFactory.getLogger(NettyClusterManager.class);
    private ClusterHost localClusterHost;
    private ClusterHostServer clusterHostServer;
    private ReentrantReadWriteLock sendMessageLock = new ReentrantReadWriteLock();
    private ReentrantReadWriteLock clusterHostLock = new ReentrantReadWriteLock();

    // region internal function
    protected boolean isLocal(ClusterHost clusterHost) {
        return StrUtil.equals(clusterHost.getIPAddress(), localClusterHost.getIPAddress())
                && clusterHost.getPort() == localClusterHost.getPort();
    }

    protected ClusterHost buildLocalClusterHost(Context context) {
        InetAddress localAddress = context.getByType("_local_address_", InetAddress.class);
        String localAddrVal = localAddress.getHostAddress();
        Integer port = context.getInteger("netty.tcp.server.port");
        return new NettyClusterHost(localAddrVal, port);
    }

    protected Context buildSpaceContext(Context parent) {
        Tuple result = localAddress();
        if (result == null) {
            throw new IllegalStateException("Not found allow IPv4 address for host.");
        }
        NetworkInterface ni = result.get(0);
        InetAddress localAddress = result.get(1);
        MapBuilder<String, Object> builder = MapBuilder.create();
        return Context.space(parent, builder.put("_network_interface_", ni)
                .put("_local_address_", localAddress)
                .build());
    }

    protected ClusterHostServer buildClusterHostServer(Context context) {
        ClusterMessageHandler clusterMessageHandler = buildClusterMessageHandler(context);
        // TODO
        return null;
    }

    protected ClusterMessageHandler buildClusterMessageHandler(Context context) {
        // TODO
        return null;
    }

    protected void sendJoinMessage(Context context) {
        // TODO
    }
    // endregion

    @Override
    public ClusterHost joinGroup(Context context) {
        if (localClusterHost != null) {
            return localClusterHost;
        }
        clusterHostLock.writeLock().lock();
        ClusterHostServer clusterHostServer = null;
        try {
            if (localClusterHost != null) {
                return localClusterHost;
            }
            Context space = buildSpaceContext(context);
            ClusterHost localClusterHost = buildLocalClusterHost(space);
            clusterHostServer = buildClusterHostServer(context);
            clusterHostServer.startup(context);
            this.clusterHostServer = clusterHostServer;
            this.localClusterHost = localClusterHost;
            return localClusterHost;
        } catch (Exception e) {
            if (clusterHostServer != null) {
                try {
                    clusterHostServer.shutdown();
                } catch (Exception ignore) {
                    log.warn("When close ClusterHostServer occurred error at joinGroup for startup ClusterHostServer.", ignore);
                }
            }
            throw new IllegalStateException(e);
        } finally {
            clusterHostLock.writeLock().unlock();
        }
    }

    @Override
    public String joinHost(ClusterHost clusterHost, Context context) {
        return null;
    }

    @Override
    public void levelGroup(Context context) {

    }

    @Override
    public void leaveHost(String hostId) {

    }

    protected void _sendMessage(ClusterHost targetHostId, byte[] content) {
        sendMessageLock.readLock().lock();

        sendMessageLock.readLock().unlock();
    }

    @Override
    public void sendMessage(String targetHostId, String content) {
    }

    @Override
    public void sendMessage(String targetHostId, byte[] content) {
    }

    @Override
    public void sendMessage(ClusterMessage clusterMessage) {

    }
}
